package com.example.demo.controller;

import com.example.demo.RabittMQ.SeckillMessage;
import com.example.demo.bean.*;
import com.example.demo.dao.GoodsDao;
import com.example.demo.dao.OrderDao;
import com.example.demo.exception.CodeMsg;
import com.example.demo.exception.GlobalException;
import com.example.demo.redis.RedisServer;
import com.example.demo.redisPrefix.GoodsPrefixAndExpireSecond;
import com.example.demo.RabittMQ.RabittMQSender;
import com.example.demo.redisPrefix.SeckillOrderPrefixAndExpireSecond;
import com.example.demo.result.Result;
import com.example.demo.service.GoodsService;
import com.example.demo.service.OrderService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

//正式进入多人点击秒杀商品
@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {

    @Autowired
    RedisServer redisServer;
    @Autowired
    GoodsDao goodsDao;




    @Autowired
    RabittMQSender rabittMQSender;

    @Autowired
    OrderService orderService;

    @Autowired
    OrderDao orderDao;


    private static HashMap<Long,Boolean> goodsSeckillAll=new HashMap<Long, Boolean>();
    private RateLimiter rateLimiter=RateLimiter.create(10);

    //先预加载要准备到东西，秒杀商品的库存数量存入redis,会在初始化Bean的时候，先扫码此afterPropertiesSet()方法
    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsSeckill> goodsSeckillList = goodsDao.findGoodsSeckill();
        //if (goodsSeckillList==null||goodsSeckillList.size()==0){
        //    throw new GlobalException(CodeMsg.GoodsSeckill_NOT_EXIST);
        //}
        //将所有要秒杀的商品存入redis，没有过滤掉秒杀过期的和秒杀还没开始的
        for (GoodsSeckill goodsSeckill : goodsSeckillList) {
            Long goodsId = goodsSeckill.getId();
            //秒杀商品存储的数量
            Integer stockCount = goodsSeckill.getStockCount();
            String goodsIdStr = goodsId.toString();

            String set = redisServer.set(GoodsPrefixAndExpireSecond.goodsSeckill, goodsIdStr, stockCount);
            System.out.println(set);

            //初始化商品，标记一下商品是否买完，减少访问数据库的压力
            goodsSeckillAll.put(goodsId,false);
        }

    }

    @RequestMapping("/do_seckill")
    @ResponseBody
    public Result<Integer> goodsSeckill(Long goodsId, User user, Model model){
        //每个秒杀的用户都会在一秒获取令牌，获取到的话就能秒杀成功，获取不到的话就会进入高峰期秒杀提示(限流)
        if (!rateLimiter.tryAcquire(1, TimeUnit.SECONDS)){
            throw new GlobalException(CodeMsg.ACCESS_LIMIT_REACHED);
        }

        if (user==null){
            throw new GlobalException(CodeMsg.SERVER_ERROR);
        }

        model.addAttribute("user",user);

        //将redis的库存减一
        Boolean flag;
        try{
            //如果没有找到是乱传的goodsId
            flag = goodsSeckillAll.get(goodsId);
        }catch (Exception e){
            e.printStackTrace();
            throw new GlobalException(CodeMsg.SERVER_ERROR);
        }

        if (flag==true){
            //代表买完了
            throw new GlobalException(CodeMsg.SECKILL_OVER);
        }

        //避免重复秒杀,查看订单表中看是否以及存入,要在redis预减之前进行,防止下单之后重复点击导致redis过减
        Order order=new Order();
        order.setGoodsId(goodsId);
        order.setUserId(user.getId());
        Order orderFind = orderDao.findOrder(order);
        if (orderFind!=null){
            throw new GlobalException(CodeMsg.REPEATE_SECKILL);//勿重复秒杀
        }

        //没有买完，redis对应秒杀商品库存减一，有一漏洞，就是当你点击秒杀之后，再点击秒杀redis的存量会减少但是数据库库存却没有改变。
        //redisServer.set(),用户点击过后存入redis,再点击之后提醒用户或者秒杀按钮变为不可点击。
        String goodsIdStr = goodsId.toString();
        Long stockStorgeLess = redisServer.decr(GoodsPrefixAndExpireSecond.goodsSeckill,goodsIdStr);
        if (stockStorgeLess<0){
            //说明当为0的时候，也递减了一，以及卖完了
            //标识可以变为true
            goodsSeckillAll.put(goodsId,true);
            throw new GlobalException(CodeMsg.SECKILL_OVER);
        }

        //进行异步下单，使用RabittMQ
        SeckillMessage seckillMessage=new SeckillMessage();
        seckillMessage.setGoodsId(goodsId);
        seckillMessage.setUser(user);

        //将信息存入RabittMQ中,等待数据库的操作，包括商品减一，存入订单
        rabittMQSender.sendMsg(seckillMessage);

        return Result.success(0);
    }

    @RequestMapping("/findResult")
    @ResponseBody
    public Result<Integer> findSeckillResult(User user, @RequestParam("goodsId") Long goodsId){
        if (user==null){
            throw new GlobalException(CodeMsg.SERVER_ERROR);
        }

        //查询秒杀结果
        Integer flag = orderService.findSeckillResultByRedis(user, goodsId);
        return Result.success(flag);
    }

}
